package model.parser;

import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
import java.util.Stack;
import java.util.Queue;

import model.semantics.AggregatorNode;
import model.semantics.Node;

import model.tokens.*;

/**
 * 
 */

public class Parser<T> {
    private Lexer<T> myLexer;
    private FunctionTable<T> myFunctionTable;
    private String myTokenFile, mySyntaxFile;

    /**
     * Constructs a parser.
     */
    public Parser(String tokenFile, String syntaxFile) {
        myTokenFile = tokenFile;
        mySyntaxFile = syntaxFile;
        myFunctionTable = new FunctionTable<T>();
        myFunctionTable.addFunctionsFromSyntaxFile(syntaxFile);
        myLexer = new Lexer<T>(tokenFile, myFunctionTable);
    }

    /**
     * Parses an expression.
     * 
     * @param expression
     *            a string expression to be parsed.
     * @return the root node of the expression tree generated for the
     *         expression.
     */
    public Node<T> parse(String expression) {
        Queue<Token<T>> tokens = myLexer
                .makeTokensFromExpressionString(expression);
        return parse(tokens);
    }

    public Node<T> parse(Queue<Token<T>> tokens) {
        Queue<Node<T>> nodes = processExpression(tokens);

        // Create aggregator node out of all things left on stack.
        List<Node<T>> commands = new ArrayList<Node<T>>(nodes.size());
        for (int i = nodes.size() - 1; i >= 0; i--) {
            commands.add(nodes.poll());
        }
        // Collections.reverse(commands);

        return new AggregatorNode<T>(commands);
    }

    /**
     * Uses the Dijkstra shunting-yard algorithm to generate an RPN output from
     * the input list of tokens, so that an abstract syntax tree can be
     * generated.
     * 
     * @param tokens
     *            a list of tokens
     * @return a queue of tokens representing the list of commands.
     */
    private Queue<Node<T>> processExpression(Queue<Token<T>> tokens) {
        Automaton<T> automaton = new Automaton<T>(tokens, myTokenFile,
                mySyntaxFile);
        automaton.processTokens();
        // Push everything from operator stack onto output queue.
        automaton.fillOutputQueue();
        return automaton.getOutput();
    }
}
